Фінальний проєкт: Аналіз даних переписів села Дунаєць за 1778 й 1897 роки¶

Єфімова Оксана

Початок роботи¶

Цей файл представляє собою аналіз даних переписів населення села Дунаєць Чернігівської губернії за 1778 та 1897 роки. Для роботи я використала бібліотеки, з якими ми працювали протягом курсу а також plotly.express, тому його необхідно завантажити.

In [3]:
%pip install plotly.express
Requirement already satisfied: plotly.express in ./.venv/lib/python3.12/site-packages (0.4.1)
Requirement already satisfied: pandas>=0.20.0 in ./.venv/lib/python3.12/site-packages (from plotly.express) (2.2.3)
Requirement already satisfied: plotly>=4.1.0 in ./.venv/lib/python3.12/site-packages (from plotly.express) (6.2.0)
Requirement already satisfied: statsmodels>=0.9.0 in ./.venv/lib/python3.12/site-packages (from plotly.express) (0.14.5)
Requirement already satisfied: scipy>=0.18 in ./.venv/lib/python3.12/site-packages (from plotly.express) (1.16.0)
Requirement already satisfied: patsy>=0.5 in ./.venv/lib/python3.12/site-packages (from plotly.express) (1.0.1)
Requirement already satisfied: numpy>=1.11 in ./.venv/lib/python3.12/site-packages (from plotly.express) (2.2.6)
Requirement already satisfied: python-dateutil>=2.8.2 in ./.venv/lib/python3.12/site-packages (from pandas>=0.20.0->plotly.express) (2.9.0.post0)
Requirement already satisfied: pytz>=2020.1 in ./.venv/lib/python3.12/site-packages (from pandas>=0.20.0->plotly.express) (2025.2)
Requirement already satisfied: tzdata>=2022.7 in ./.venv/lib/python3.12/site-packages (from pandas>=0.20.0->plotly.express) (2025.2)
Requirement already satisfied: narwhals>=1.15.1 in ./.venv/lib/python3.12/site-packages (from plotly>=4.1.0->plotly.express) (1.42.1)
Requirement already satisfied: packaging in ./.venv/lib/python3.12/site-packages (from plotly>=4.1.0->plotly.express) (25.0)
Requirement already satisfied: six>=1.5 in ./.venv/lib/python3.12/site-packages (from python-dateutil>=2.8.2->pandas>=0.20.0->plotly.express) (1.17.0)

[notice] A new release of pip is available: 23.2.1 -> 25.1.1
[notice] To update, run: pip install --upgrade pip
Note: you may need to restart the kernel to use updated packages.

Імпорт усі необхідних бібліотек

In [2]:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import altair as alt
import plotly.express as px

Дані про мешканців села 1778 року імпортовано в DataFrame, створено копію для роботи, змінено формат назв стовпців для зручності аналізу.

In [4]:
pop_1778 = pd.read_csv('2.2_Дунаєць_1778(2).xlsx - База.csv')
pop_1778 = pop_1778.copy()

pop_1778.rename(columns={'Unnamed: 10': 'rus_surname', 'Unnamed: 15': 'age', 'Unnamed: 6': 'men', 'Unnamed: 14': 'status', 'Unnamed: 2': 'family_num', 'Unnamed: 11': 'family_status_rus'}, inplace=True)

pop_1778
Out[4]:
Unnamed: 0 Unnamed: 1 family_num Unnamed: 3 Unnamed: 4 Unnamed: 5 men Unnamed: 7 Unnamed: 8 Unnamed: 9 rus_surname family_status_rus Unnamed: 12 Unnamed: 13 status age Unnamed: 16 Unnamed: 17 Unnamed: 18
0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1 Аркуш NaN NaN NaN Число людей наскрізне NaN Число людей джерело NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2 NaN ID наскрізна ID домогосподарство Двори джерело Чоловіки Жінки Чоловіки Жінки Імʼя По-батькові Прізвище Родиний статус Категорія Клас Соціальний статус Вік Був на сповіді Не був Не був за малолітством
3 445 1 1 1 1 NaN 1 NaN Федор Кондратьев Лукашевич господар nuclear 3b духовные 44 х NaN NaN
4 NaN 2 NaN NaN NaN 1 NaN 1 Варвара Симева NaN жена NaN NaN духовные 35 х NaN NaN
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
1595 NaN 1593 104 12 824 NaN 236 NaN Леонтий Афанасиев NaN двоюродный брат Александра nuclear 3a бездворные 25 х NaN NaN
1596 NaN 1594 NaN NaN NaN 770 NaN 222 Мотрона Михайлова NaN жена его NaN NaN бездворные 22 х NaN NaN
1597 NaN 1595 105 13 825 NaN 237 NaN Герасим Фомик Мерник господар nuclear 3b бездворные 43 х NaN NaN
1598 NaN 1596 NaN NaN NaN 771 NaN 223 Агафия Максимова NaN жена его NaN NaN бездворные 42 х NaN NaN
1599 NaN 1597 NaN NaN 826 NaN 238 NaN Лукьян NaN NaN NaN NaN NaN бездворные 14 х NaN NaN

1600 rows × 19 columns

Аналогічну операцію проведено з іншим датасетом

In [5]:
data_1897 = pd.read_csv('2.1_Дунаєць.xlsx - База листов.csv')
pop_1897 = pd.read_csv('2.1_Дунаєць.xlsx - База людей.csv')
pop_1897 = pop_1897.copy()

pop_1897.rename(columns = {'Пол': 'gender', 'Возраст': 'age'}, inplace = True)

pop_1897
Out[5]:
Кто заполнял базу ID строки в базе List ID Link ID Домохозяйствао ID жилец ФИО gender Глава хозяйства и глава семьи age ... Здесь ли обыкновенно проживает Отметка об отсуствии Вероисповедание Родной язык Умеет ли читать Обучение Профессия главное Профессия вспомогательное Положение по воинской повинности Примітки
0 Podhorna 1.0 F.132.Op.1.Spr.1343. Арк.1-2 https://www.familysearch.org/ark:/61903/3:1:3Q... 1.0 1.0 Krasovskij Pantelejmon Ivanovich m husband 32 ... 1 NaN orthodox ukr 1.0 rural school farmer NaN Nizhnij chin zapasu NaN
1 Podhorna 2.0 F.132.Op.1.Spr.1343. Арк.1-2 https://www.familysearch.org/ark:/61903/3:1:3Q... 1.0 2.0 Krasovskaya Stefanida Artemieva f wife 28 ... 1 NaN orthodox ukr 0.0 NaN farmer with husband NaN NaN NaN
2 Podhorna 3.0 F.132.Op.1.Spr.1343. Арк.1-2 https://www.familysearch.org/ark:/61903/3:1:3Q... 1.0 3.0 Krasovskaya Anna Pantelejmonova f daughter 5 ... 1 NaN orthodox ukr 0.0 NaN with father NaN NaN NaN
3 Podhorna 4.0 F.132.Op.1.Spr.1343. Арк.1-2 https://www.familysearch.org/ark:/61903/3:1:3Q... 1.0 4.0 Krasovskij Stefan Pantelejmonov m son 1 ... 1 NaN orthodox ukrm 0.0 NaN with father NaN NaN NaN
4 Podhorna 5.0 F.132.Op.1.Spr.1343. Арк.1-2 https://www.familysearch.org/ark:/61903/3:1:3Q... 1.0 5.0 Krasovskij Ivan Kondratov m father 70 ... 1 NaN orthodox ukr 0.0 NaN with son NaN NaN Slep na oba hlaza 10 let nazad
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
1547 Podhorna 1548.0 F.132.Op.1.Spr.1343. Арк.550-551 https://www.familysearch.org/ark:/61903/3:1:3Q... 261.0 6.0 Gricunov Mikhail Sergeev m son 3 ... 1 NaN orthodox ukr 0.0 NaN with father NaN NaN NaN
1548 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1549 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1550 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1551 * - В оригінальному джерелі ячейка "мова" була... NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN

1552 rows × 25 columns

Вікова структура¶

Порівняння вікової структури населення¶

Для початку було проведено аналіз вікової структури населення, згідно з даними обох переписів. Колонки з обох датасетів були перейменовані таким чином, щоб ними можна було однаково кооперувати. З цією ж метою створено колонку gender у pop_1778, щоб вона була аналогічна розподілу на статі в pop_1897. Також необхідно було почистити перші рядки датасету перепису 1778 року, оскільки його структура дещо відрізняється від іншого.

In [6]:
pop_1778['gender'] = pop_1778.iloc[3:].men.apply(lambda x: 'm' if pd.notna(x) else 'f')
ages_1778 = pop_1778.age.iloc[3:].dropna()

ages_1897 = pop_1897.age.dropna()

Наступним було створено окремий датафрейм, який містить лише стовпці з віком, додамо стовпець із роком для кожного значення і поєднано датафрейми за допомогою pd.concat().

In [7]:
ages_1778 = ages_1778.to_frame(name='age')
ages_1778['year'] = '1778'

ages_1897 = ages_1897.to_frame(name='age')
ages_1897['year'] = '1897'

ages_concat = pd.concat([ages_1778, ages_1897])
ages_concat
Out[7]:
age year
3 44 1778
4 35 1778
5 17 1778
6 15 1778
7 14 1778
... ... ...
1543 30 1897
1544 9 1897
1545 7 1897
1546 5 1897
1547 3 1897

3142 rows × 2 columns

Тепер отримано датафрейм, який складається з віку осіб, де половина відноситься до 1778 року, а інша -- до 1897. Це дає змогу побудувати гістограму. Для цього я використовую бібліотеку Altair. Гістограма побудована таким чином, що на кожному відрізку можна дослідити різницю між обома переписами, оскільки графіки накладені.

In [8]:
alt.Chart(ages_concat).mark_bar(opacity=0.5,binSpacing=0).encode(alt.X('age:Q', title = 'Вік').bin(maxbins=20),alt.Y('count()', title = 'Кількість').stack(None), alt.Color('year:N', title='Рік')).properties(width=600,height=400,title='Розподіл віку у 1778 та 1897 роках')
Out[8]:

Результат показує, що у 1778 році народжуваність була досить високою, а тривалість життя низькою, що є нормою для цього часу. Цей період в с.Дунаєць припав на поступову ліквідацію козацтва та до запровадження кріпацтва для селян. Можливою причиною спаду частки дитячого населення, а саме осіб від 5 до 15 років може бути дитяча смертність, оскільки ця категорія найбільш вразлива до хвороб. Щодо 1897 року, то спостерігається помітне зростання тривалості життя, графік є плавнішим, ніж попередній, народжуваність дещо впала, пояснювати це може той факт, що після відміни кріпосного права, землі не вистачало на всіх, тому це було б не вигідно для сімей. Імовірно, це могло би бути причиною різкого спаду молодого населення (20-25 років), які мігрували в пошуках кращого життя.

Порівняння статевого співвідношення вікових груп¶

Отримавши такі дані, було проведено ще віковий розподіл населення за статтю. Для цього було розроблено функцію population_pyramid_builder(df, year), де df -- датафрейм, який буде аналізуватися, year -- рік, потрібен для формування заголовка до піраміди. Оскільки побудова пірамід для обох років потребує багато повторюваних операцій, функції можуть скоротити довжину коду і підвищити читабельність.

In [9]:
def age_group(age):
    if pd.isna(age):
        return None
    age_num = pd.to_numeric(age, errors='coerce')
    if pd.isna(age_num):
        return None
    elif isinstance(age, str) and any(term in age for term in ['month', 'months', 'day', 'days', 'week', 'weeks']) or age_num < 5:
        return '0-4'
    elif isinstance(age, str) and any(term in age for term in ['month', 'months', 'day', 'days', 'week', 'weeks']) or 4 < age_num < 10:
        return '5-9'
    elif age_num % 10 <= 4:
        lower = (age_num // 10) * 10
        return f'{lower}-{lower + 4}'
    else:
        lower = (age_num // 10) * 10 + 5
        return f'{lower}-{lower + 4}'
In [19]:
pop_1778['age_group'] = pop_1778['age'].apply(age_group)
pop_1778 = pop_1778[pop_1778['age_group'].notna()].copy()

pop_1897['age_group'] = pop_1897['age'].apply(age_group)
pop_1897 = pop_1897[pop_1897['age_group'].notna()].copy()

unique_ages_list = []

unique_ages_list.append(pop_1778['age_group'].dropna().drop_duplicates().to_list())
unique_ages_list.append(pop_1897['age_group'].dropna().drop_duplicates().to_list())

common_order = max(unique_ages_list, key = len)
common_order = sorted(common_order, key=lambda x: int(x.split('-')[0]), reverse=True)
common_order_s = pd.Series(common_order)
In [20]:
def population_pyramid_builder(df, year):

    df['age_group_lower'] = df['age_group'].apply(lambda x: x.split('-')[0] if isinstance(x, str) else None)
    df['age_group_lower'] = df['age_group_lower'].str.strip()
    df = df.sort_values(by = 'age_group_lower', key=lambda x: pd.to_numeric(x), ascending = False)
    df.drop(columns='age_group_lower', inplace=True)

    males = df[(df['gender'] == 'm')]
    females = df[(df['gender'] == 'f')]

    male = alt.Chart(males).mark_bar(color='#1f77b4').encode(x=alt.X('count(age_group):Q', title = 'Чоловіки', scale=alt.Scale(reverse=True, domainMax=120)),y=alt.Y("age_group:O", sort=common_order, axis=None), tooltip = alt.Tooltip(['count(age_group)'], title = "Кількість"))

    middle = alt.Chart(common_order_s.to_frame(name='age_group')).mark_text().encode(y=alt.Y('age_group:O', sort=common_order, axis=None),text=alt.Text('age_group:O'))

    female = alt.Chart(females).mark_bar(color = '#e377c2').encode(x=alt.X('count(age_group):Q', title = 'Жінки'), y= alt.Y("age_group:O", sort = common_order, axis=None), tooltip = alt.Tooltip(['count(age_group)'], title = "Кількість"))

    gender_pyramid = alt.concat(male, middle, female, spacing=1).resolve_scale(y='shared').properties(title=alt.TitleParams(text=f'Віковий розподіл населення за статтю ({year})', anchor='middle'))

    return gender_pyramid
In [21]:
pyramid_1778 = population_pyramid_builder(pop_1778, 1778)
pyramid_1897 = population_pyramid_builder(pop_1897, 1897)
alt.hconcat(pyramid_1778, pyramid_1897).resolve_scale(y='shared')
Out[21]:

Представлені результати показують, що у 1778 році частка дівчат віком від 5 до 9 років є удвічі меншою ніж віком до 4 років, що може вказувати на високий рівень дитячої смертності, особливо серед дівчат. У піраміді за 1897 рік помітна істотна різниця між часткою хлопчиків віком до 4 років та дівчатками того ж віку. Можливою причиною цьому може бути вразливість до хвороб. Загалом же піраміда є доволі симетричною і вказує на демографічну стабільність.

Соціальна структура¶

Порівняння соціальної структури сімей¶

Село Дунаєць, за писемними джерелами, засноване в першій половині 17 ст. глухівським козаком Радьком, та заселено було в основному вільними козаками. (вікіпедія)

Як уже зазначалося в контексті ліквідації козацького устрою, було проведено аналіз соціальної структури сімей за обома переписами. У базі листів перепису 1778 року присутні такі верстви населення, як військові, духовенство та посполиті й бездвірні. Проаналізувавши дані з документа "2.2_Дунаєць_1778(2).xlsx - Пояснення та фінальні таблиці.csv", стає зрозуміло, що з 35 сімей, зарахованих до категорії військових, 34 -- мають козацький статус, а 1 -- родина відставного майора. Для підтвердження результатів було зроблено перевірку кількості сімей зі статусом "військові".

In [240]:
pop_1778[pop_1778['status'] == 'военные']['family_num'].value_counts().sum()
Out[240]:
np.int64(35)

Що й збігається з результатами документу "2.2.2_Структура родини Дунаєць_1778.xlsx - Военные.csv"

Натомість база листів 1897 року поділяє родини на наступні групи: козаки, духовенство, дворяни й селяни. Така різниця між даними двох переписів цілком має сенс, адже за століття відбулися значні зміни у соціальному становищі імперії. З метою кращого порівняння між результатами розподілу, було вирішено виокремити родину майора в окремий датафрейм, а решту позначити як козаків. Іншф категорії звести до відповідних не можна, тому вони залишуться початковими.

In [241]:
cossacks_1778 = pd.read_csv('2.2.2_Структура родини Дунаєць_1778.xlsx - Военные.csv')
cossacks_1778.at[5, 'Кількість'] -= 1

major_1778 = cossacks_1778.copy()
major_1778[['Кількість', '%', '% кожного класу', 'Усього по категоріях']] = 0
major_1778.at[5, 'Кількість'] = 1

dukhovni_1778 = pd.read_csv('2.2.2_Структура родини Дунаєць_1778.xlsx - Духовные.csv')
pospolyti_1778 = pd.read_csv('2.2.2_Структура родини Дунаєць_1778.xlsx - Посполитые+бездворные.csv')

total_1778 = pd.read_csv('2.2.2_Структура родини Дунаєць_1778.xlsx - Total.csv')

church_data_1778 = pd.read_csv('2.2_Дунаєць_1778(2).xlsx - Пояснення та фінальні таблиці.csv')
In [242]:
cossacks_1897 = pd.read_csv('2.1.1_Структура родини Дунаєць_1897.xlsx - Cossacks.csv')

dukhovni_1897 = pd.read_csv('2.1.1_Структура родини Дунаєць_1897.xlsx - Dukhovnogo.csv')

nobels_1897 = pd.read_csv('2.1.1_Структура родини Дунаєць_1897.xlsx - Nobles+imenuyushchijsya dvoryan.csv')

peasants_1897 = pd.read_csv('2.1.1_Структура родини Дунаєць_1897.xlsx - Peasant-owner.csv')

total_1897 = pd.read_csv('2.1.1_Структура родини Дунаєць_1897.xlsx - Total.csv')

Функція status_filter(df, cat) була створена, для того щоб створити окремі датасети які мають спільну частину по стовпцях "Категорія" і "Клас" та відрізняються за кількістю, яка позначена відповідним статусом, переданим cat. Такий підхід дозволяє уникнути переназивання значень при подальшій візуалізації.

In [243]:
def status_filter(df, cat):
    df = df.copy()
    df['Клас'] = df['Клас'].str[2:]
    df_count = df[['Категорія', 'Клас', 'Кількість']].iloc[:-1]
    df_count.rename(columns = {'Кількість': cat}, inplace = True)
    return df_count
In [244]:
cossacks_count_1778 = status_filter(cossacks_1778, 'Козаки')
major_count_1778 = status_filter(major_1778, 'Офіцери PIA')
dukhovni_count_1778 = status_filter(dukhovni_1778, 'Духовенство')
pospolyti_count_1778 = status_filter(pospolyti_1778, 'Посполиті й бездвірні')
In [245]:
cossacks_count_1897 = status_filter(cossacks_1897, 'Козаки')
dukhovni_count_1897 = status_filter(dukhovni_1897, 'Духовенство')
nobels_count_1897 = status_filter(nobels_1897, 'Дворяни')
peasants_count_1897 = status_filter(peasants_1897, 'Селяни')

Для візуалізації такого набору даних я обрала sunburst chart, тому для цього потрібно змерджити дані у всіх листах за класом і категорією.

In [246]:
merged_cos_dukh_1778 = pd.merge(cossacks_count_1778, dukhovni_count_1778, on = ['Категорія', 'Клас'], how = 'left')
merged_cos_dukh_maj_1778 = pd.merge(merged_cos_dukh_1778, major_count_1778, on = ['Категорія', 'Клас'], how = 'left')
merged_1778 = merged_cos_dukh_maj_1778.merge(pospolyti_count_1778, on = ['Категорія', 'Клас'], how = 'left')

merged_1778
Out[246]:
Категорія Клас Козаки Духовенство Офіцери PIA Посполиті й бездвірні
0 Самотні особи Вдови і вдівці 0 0 0 0
1 NaN Нежонаті, або неокресленого цивільного стану 0 0 0 0
2 Безструктурні Нежонаті брати і сестри, які живуть разом 0 0 0 0
3 NaN Інші кревні, які живуть разом 0 0 0 0
4 Нуклеарні Шлюбі пари без дітей 0 0 0 1
5 NaN Шлюбні пари з дітьми 1 2 1 15
6 NaN Вдівці з дітьми 0 0 0 0
7 NaN Вдови з дітьми 0 0 0 1
8 Розширені Родини з висхідним розширенням 0 0 0 1
9 NaN Родини з низхідним розширенням 0 0 0 0
10 NaN Родини з бічним розширенням 0 0 0 1
11 NaN Комбінація 4a-4c 0 0 0 0
12 Мультифокальні Родини з висхідними другорядними ядрами 0 0 0 1
13 NaN Родини з низхідними другорядними ядрами 2 0 0 8
14 NaN Родини з бічними другорядними ядрами 2 0 0 5
15 NaN Братські родини 3 0 0 9
16 NaN Інші 26 0 0 32
In [247]:
merged_cos_dukh_1897 = pd.merge(cossacks_count_1897, dukhovni_count_1897, on = ['Категорія', 'Клас'], how = 'left')
merged_cos_dukh_nob_1897 = pd.merge(merged_cos_dukh_1897, nobels_count_1897, on = ['Категорія', 'Клас'], how = 'left')
merged_1897 = merged_cos_dukh_nob_1897.merge(peasants_count_1897, on = ['Категорія', 'Клас'], how = 'left')

merged_1897
Out[247]:
Категорія Клас Козаки Духовенство Дворяни Селяни
0 Самотні особи Вдови і вдівці 1 0 0 2
1 NaN Нежонаті, або неокресленого цивільного стану 0 0 1 1
2 Безструктурні Нежонаті брати і сестри, які живуть разом 0 0 0 0
3 NaN Інші кревні, які живуть разом 1 0 0 0
4 Нуклеарні Шлюбі пари без дітей 4 0 0 7
5 NaN Шлюбні пари з дітьми 54 1 0 32
6 NaN Вдівці з дітьми 1 0 0 2
7 NaN Вдови з дітьми 4 0 0 8
8 Розширені Родини з висхідним розширенням 16 1 0 13
9 NaN Родини з низхідним розширенням 3 0 0 1
10 NaN Родини з бічним розширенням 3 0 0 11
11 NaN Комбінація 4a-4c 2 0 0 2
12 Мультифокальні Родини з висхідними другорядними ядрами 6 0 0 2
13 NaN Родини з низхідними другорядними ядрами 36 0 1 24
14 NaN Родини з бічними другорядними ядрами 4 0 0 2
15 NaN Братські родини 7 0 0 2
16 NaN Інші 4 0 0 1

Застосовано pd.ffill(), щоб у кожному рядку було значення категорії

In [248]:
merged_1778['Категорія'] = merged_1778['Категорія'].ffill()

merged_1778
Out[248]:
Категорія Клас Козаки Духовенство Офіцери PIA Посполиті й бездвірні
0 Самотні особи Вдови і вдівці 0 0 0 0
1 Самотні особи Нежонаті, або неокресленого цивільного стану 0 0 0 0
2 Безструктурні Нежонаті брати і сестри, які живуть разом 0 0 0 0
3 Безструктурні Інші кревні, які живуть разом 0 0 0 0
4 Нуклеарні Шлюбі пари без дітей 0 0 0 1
5 Нуклеарні Шлюбні пари з дітьми 1 2 1 15
6 Нуклеарні Вдівці з дітьми 0 0 0 0
7 Нуклеарні Вдови з дітьми 0 0 0 1
8 Розширені Родини з висхідним розширенням 0 0 0 1
9 Розширені Родини з низхідним розширенням 0 0 0 0
10 Розширені Родини з бічним розширенням 0 0 0 1
11 Розширені Комбінація 4a-4c 0 0 0 0
12 Мультифокальні Родини з висхідними другорядними ядрами 0 0 0 1
13 Мультифокальні Родини з низхідними другорядними ядрами 2 0 0 8
14 Мультифокальні Родини з бічними другорядними ядрами 2 0 0 5
15 Мультифокальні Братські родини 3 0 0 9
16 Мультифокальні Інші 26 0 0 32
In [249]:
merged_1897['Категорія'] = merged_1897['Категорія'].ffill()

merged_1897
Out[249]:
Категорія Клас Козаки Духовенство Дворяни Селяни
0 Самотні особи Вдови і вдівці 1 0 0 2
1 Самотні особи Нежонаті, або неокресленого цивільного стану 0 0 1 1
2 Безструктурні Нежонаті брати і сестри, які живуть разом 0 0 0 0
3 Безструктурні Інші кревні, які живуть разом 1 0 0 0
4 Нуклеарні Шлюбі пари без дітей 4 0 0 7
5 Нуклеарні Шлюбні пари з дітьми 54 1 0 32
6 Нуклеарні Вдівці з дітьми 1 0 0 2
7 Нуклеарні Вдови з дітьми 4 0 0 8
8 Розширені Родини з висхідним розширенням 16 1 0 13
9 Розширені Родини з низхідним розширенням 3 0 0 1
10 Розширені Родини з бічним розширенням 3 0 0 11
11 Розширені Комбінація 4a-4c 2 0 0 2
12 Мультифокальні Родини з висхідними другорядними ядрами 6 0 0 2
13 Мультифокальні Родини з низхідними другорядними ядрами 36 0 1 24
14 Мультифокальні Родини з бічними другорядними ядрами 4 0 0 2
15 Мультифокальні Братські родини 7 0 0 2
16 Мультифокальні Інші 4 0 0 1

Переведено у довгий формат для збільшення кількості "корінців" під кожною категорією

In [250]:
melt_1778 = merged_1778.melt(id_vars=['Категорія', 'Клас'],value_vars=['Козаки', 'Духовенство', 'Офіцери PIA', 'Посполиті й бездвірні'],var_name='group',value_name='count')

melt_1778
Out[250]:
Категорія Клас group count
0 Самотні особи Вдови і вдівці Козаки 0
1 Самотні особи Нежонаті, або неокресленого цивільного стану Козаки 0
2 Безструктурні Нежонаті брати і сестри, які живуть разом Козаки 0
3 Безструктурні Інші кревні, які живуть разом Козаки 0
4 Нуклеарні Шлюбі пари без дітей Козаки 0
... ... ... ... ...
63 Мультифокальні Родини з висхідними другорядними ядрами Посполиті й бездвірні 1
64 Мультифокальні Родини з низхідними другорядними ядрами Посполиті й бездвірні 8
65 Мультифокальні Родини з бічними другорядними ядрами Посполиті й бездвірні 5
66 Мультифокальні Братські родини Посполиті й бездвірні 9
67 Мультифокальні Інші Посполиті й бездвірні 32

68 rows × 4 columns

In [251]:
melt_1897 = merged_1897.melt(id_vars=['Категорія', 'Клас'],value_vars=['Козаки', 'Духовенство', 'Дворяни', 'Селяни'],var_name='group',value_name='count')

melt_1897
Out[251]:
Категорія Клас group count
0 Самотні особи Вдови і вдівці Козаки 1
1 Самотні особи Нежонаті, або неокресленого цивільного стану Козаки 0
2 Безструктурні Нежонаті брати і сестри, які живуть разом Козаки 0
3 Безструктурні Інші кревні, які живуть разом Козаки 1
4 Нуклеарні Шлюбі пари без дітей Козаки 4
... ... ... ... ...
63 Мультифокальні Родини з висхідними другорядними ядрами Селяни 2
64 Мультифокальні Родини з низхідними другорядними ядрами Селяни 24
65 Мультифокальні Родини з бічними другорядними ядрами Селяни 2
66 Мультифокальні Братські родини Селяни 2
67 Мультифокальні Інші Селяни 1

68 rows × 4 columns

Для побудови цього типу графіка найзручніше використовувати бібліотеку plotly.

In [252]:
fig = px.sunburst(melt_1778, path=['group', 'Категорія', 'Клас'], values='count', title= 'Соціальна структура сімей (1778)',width=700,height=700)

fig.update_layout(font_family="Comic Sans MS", title_font_size=25, title_x=0.5)

fig.show()
In [253]:
fig = px.sunburst(melt_1897, path=['group', 'Категорія', 'Клас'], values='count', title= 'Соціальна структура сімей (1897)',width=700,height=700)

fig.update_layout(font_family="Comic Sans MS", title_font_size=25, title_x=0.5)

fig.show()

З огляду на результати обох візуалізацій, можна зробити висновок, що кількість козаків дещо зросла, а саме козацтво ліквідували ще століття до того. Однак попри це, козаки залишалися ще окремою групою, відмінною від селян і дворян. Вони були зобовязані відбувати службу і мали свої привілеї. Отже, і з огляду на це родини частіше мали дітей.

Співвідношення соціального статусу до сімейного¶

Оскільки в переписі 1778 року немає колонки, яка стосується сімейного статусу, її було створено на базі колонки про статус у сім'ї. Для цього сформована функція, яка перебиратиме значення до 3 категорій: вдовець/а, неодружений/а, одружений/а

In [254]:
def fam_status_definer(status):
    if isinstance(status, str) and any(term in status for term in ['вдов','вдова','удова']):
        return 'вдовець/a'
    elif isinstance(status, str) and any(term in status for term in ["племянник",'дочь','доч','сын','племянница']):
        return 'неодружений/а'
    elif isinstance(status, str) and any(term in status for term in ["муж",'жена','невестка','господар', 'брат', 'дядя', 'зять']):
        return 'одружений/а'
    else:
        return None

Для іншого датасету, в якому є сімейний статус, значення були перекладені на відповідні

In [255]:
pop_1897['Семейный статус'].replace(['vidov', 'unmarried', 'married'], ['вдовець/a', 'неодружений/а', 'одружений/а'], inplace = True)
pop_1897
/var/folders/k1/s2xy8pbx3h991jnqmnthbtzh0000gn/T/ipykernel_57806/2445264546.py:1: FutureWarning:

A value is trying to be set on a copy of a DataFrame or Series through chained assignment using an inplace method.
The behavior will change in pandas 3.0. This inplace method will never work because the intermediate object on which we are setting values always behaves as a copy.

For example, when doing 'df[col].method(value, inplace=True)', try using 'df.method({col: value}, inplace=True)' or df[col] = df[col].method(value) instead, to perform the operation inplace on the original object.



Out[255]:
Кто заполнял базу ID строки в базе List ID Link ID Домохозяйствао ID жилец ФИО gender Глава хозяйства и глава семьи age ... Отметка об отсуствии Вероисповедание Родной язык Умеет ли читать Обучение Профессия главное Профессия вспомогательное Положение по воинской повинности Примітки age_group
0 Podhorna 1.0 F.132.Op.1.Spr.1343. Арк.1-2 https://www.familysearch.org/ark:/61903/3:1:3Q... 1.0 1.0 Krasovskij Pantelejmon Ivanovich m husband 32 ... NaN orthodox ukr 1.0 rural school farmer NaN Nizhnij chin zapasu NaN 30-34
1 Podhorna 2.0 F.132.Op.1.Spr.1343. Арк.1-2 https://www.familysearch.org/ark:/61903/3:1:3Q... 1.0 2.0 Krasovskaya Stefanida Artemieva f wife 28 ... NaN orthodox ukr 0.0 NaN farmer with husband NaN NaN NaN 25-29
2 Podhorna 3.0 F.132.Op.1.Spr.1343. Арк.1-2 https://www.familysearch.org/ark:/61903/3:1:3Q... 1.0 3.0 Krasovskaya Anna Pantelejmonova f daughter 5 ... NaN orthodox ukr 0.0 NaN with father NaN NaN NaN 5-9
3 Podhorna 4.0 F.132.Op.1.Spr.1343. Арк.1-2 https://www.familysearch.org/ark:/61903/3:1:3Q... 1.0 4.0 Krasovskij Stefan Pantelejmonov m son 1 ... NaN orthodox ukrm 0.0 NaN with father NaN NaN NaN 0-4
4 Podhorna 5.0 F.132.Op.1.Spr.1343. Арк.1-2 https://www.familysearch.org/ark:/61903/3:1:3Q... 1.0 5.0 Krasovskij Ivan Kondratov m father 70 ... NaN orthodox ukr 0.0 NaN with son NaN NaN Slep na oba hlaza 10 let nazad 70-74
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
1547 Podhorna 1548.0 F.132.Op.1.Spr.1343. Арк.550-551 https://www.familysearch.org/ark:/61903/3:1:3Q... 261.0 6.0 Gricunov Mikhail Sergeev m son 3 ... NaN orthodox ukr 0.0 NaN with father NaN NaN NaN 0-4
1548 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN None
1549 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN None
1550 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN None
1551 * - В оригінальному джерелі ячейка "мова" була... NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN None

1552 rows × 26 columns

Для обох датасетів значення соціальної групи є зведеними до ширших груп і перекладеними

In [256]:
pop_1778['fam_status'] = pop_1778['family_status_rus'].apply(fam_status_definer)
pop_1778['status'] = pop_1778['status'].replace({'бездворные': 'бездвірні','военные': 'козаки','дворовые': 'селяни','духовные': 'духовні','посполитые': 'посполиті'})
pop_1778
Out[256]:
Unnamed: 0 Unnamed: 1 family_num Unnamed: 3 Unnamed: 4 Unnamed: 5 men Unnamed: 7 Unnamed: 8 Unnamed: 9 ... Unnamed: 12 Unnamed: 13 status age Unnamed: 16 Unnamed: 17 Unnamed: 18 gender age_group fam_status
0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN None None
1 Аркуш NaN NaN NaN Число людей наскрізне NaN Число людей джерело NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN None None
2 NaN ID наскрізна ID домогосподарство Двори джерело Чоловіки Жінки Чоловіки Жінки Імʼя По-батькові ... Категорія Клас Соціальний статус Вік Був на сповіді Не був Не був за малолітством NaN None None
3 445 1 1 1 1 NaN 1 NaN Федор Кондратьев ... nuclear 3b духовні 44 х NaN NaN m 40-44 одружений/а
4 NaN 2 NaN NaN NaN 1 NaN 1 Варвара Симева ... NaN NaN духовні 35 х NaN NaN f 35-39 одружений/а
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
1595 NaN 1593 104 12 824 NaN 236 NaN Леонтий Афанасиев ... nuclear 3a бездвірні 25 х NaN NaN m 25-29 одружений/а
1596 NaN 1594 NaN NaN NaN 770 NaN 222 Мотрона Михайлова ... NaN NaN бездвірні 22 х NaN NaN f 20-24 одружений/а
1597 NaN 1595 105 13 825 NaN 237 NaN Герасим Фомик ... nuclear 3b бездвірні 43 х NaN NaN m 40-44 одружений/а
1598 NaN 1596 NaN NaN NaN 771 NaN 223 Агафия Максимова ... NaN NaN бездвірні 42 х NaN NaN f 40-44 одружений/а
1599 NaN 1597 NaN NaN 826 NaN 238 NaN Лукьян NaN ... NaN NaN бездвірні 14 х NaN NaN m 10-14 None

1600 rows × 22 columns

In [257]:
pop_1897['Сословие, состояние или звание'] = pop_1897['Сословие, состояние или звание'].replace({'citizen': 'містяни','cossack': 'козаки','dukhovnogo': 'духовні', 'nobles': 'дворяни','peasant-owner': 'селяни','dvorovyj': 'селяни','state peasant': 'селяни','imenuyushchijsya dvoryanin': 'дворяни'})
pop_1897
Out[257]:
Кто заполнял базу ID строки в базе List ID Link ID Домохозяйствао ID жилец ФИО gender Глава хозяйства и глава семьи age ... Отметка об отсуствии Вероисповедание Родной язык Умеет ли читать Обучение Профессия главное Профессия вспомогательное Положение по воинской повинности Примітки age_group
0 Podhorna 1.0 F.132.Op.1.Spr.1343. Арк.1-2 https://www.familysearch.org/ark:/61903/3:1:3Q... 1.0 1.0 Krasovskij Pantelejmon Ivanovich m husband 32 ... NaN orthodox ukr 1.0 rural school farmer NaN Nizhnij chin zapasu NaN 30-34
1 Podhorna 2.0 F.132.Op.1.Spr.1343. Арк.1-2 https://www.familysearch.org/ark:/61903/3:1:3Q... 1.0 2.0 Krasovskaya Stefanida Artemieva f wife 28 ... NaN orthodox ukr 0.0 NaN farmer with husband NaN NaN NaN 25-29
2 Podhorna 3.0 F.132.Op.1.Spr.1343. Арк.1-2 https://www.familysearch.org/ark:/61903/3:1:3Q... 1.0 3.0 Krasovskaya Anna Pantelejmonova f daughter 5 ... NaN orthodox ukr 0.0 NaN with father NaN NaN NaN 5-9
3 Podhorna 4.0 F.132.Op.1.Spr.1343. Арк.1-2 https://www.familysearch.org/ark:/61903/3:1:3Q... 1.0 4.0 Krasovskij Stefan Pantelejmonov m son 1 ... NaN orthodox ukrm 0.0 NaN with father NaN NaN NaN 0-4
4 Podhorna 5.0 F.132.Op.1.Spr.1343. Арк.1-2 https://www.familysearch.org/ark:/61903/3:1:3Q... 1.0 5.0 Krasovskij Ivan Kondratov m father 70 ... NaN orthodox ukr 0.0 NaN with son NaN NaN Slep na oba hlaza 10 let nazad 70-74
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
1547 Podhorna 1548.0 F.132.Op.1.Spr.1343. Арк.550-551 https://www.familysearch.org/ark:/61903/3:1:3Q... 261.0 6.0 Gricunov Mikhail Sergeev m son 3 ... NaN orthodox ukr 0.0 NaN with father NaN NaN NaN 0-4
1548 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN None
1549 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN None
1550 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN None
1551 * - В оригінальному джерелі ячейка "мова" була... NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN None

1552 rows × 26 columns

Після цього значення погруповані за кількістю пари категорій

In [258]:
grouped_1778 = pop_1778.groupby(['fam_status', 'status']).size().reset_index(name='count')
grouped_1778.sort_values(by=['count'], ascending=False, inplace=True)
grouped_1778
Out[258]:
fam_status status count
6 неодружений/а козаки 437
7 неодружений/а посполиті 382
12 одружений/а посполиті 292
11 одружений/а козаки 256
4 неодружений/а бездвірні 44
9 одружений/а бездвірні 34
1 вдовець/a козаки 27
2 вдовець/a посполиті 22
8 неодружений/а селяни 22
13 одружений/а селяни 13
5 неодружений/а духовні 7
0 вдовець/a бездвірні 4
10 одружений/а духовні 3
3 вдовець/a селяни 2
In [259]:
grouped_1897 = pop_1897.groupby(['Семейный статус', 'Сословие, состояние или звание']).size().reset_index(name='count')
grouped_1897.sort_values('count', ascending=False, inplace=True)
grouped_1897['Сословие, состояние или звание'].unique()
grouped_1897
Out[259]:
Семейный статус Сословие, состояние или звание count
5 неодружений/а козаки 448
10 одружений/а козаки 365
7 неодружений/а селяни 354
12 одружений/а селяни 246
1 вдовець/a козаки 57
2 вдовець/a селяни 43
4 неодружений/а духовні 8
3 неодружений/а дворяни 6
8 одружений/а дворяни 5
6 неодружений/а містяни 4
9 одружений/а духовні 4
11 одружений/а містяни 3
0 вдовець/a духовні 1

Для побудови цієї візуалізації я використала бібліотеку Altair і зобразила у формі heat map. При наведенні курсора на значення показує точну кількість осіб певної категорії

In [260]:
status_heatmap_1778 = alt.Chart(grouped_1778).mark_rect().encode(x=alt.X('fam_status:O', title='Сімейний статус'), y=alt.Y('status:O', title='Соціальна група'), color='count:Q', tooltip=['count']).properties(title = '1778', height = 200, width = 100).interactive()
In [261]:
status_heatmap_1897 = alt.Chart(grouped_1897).mark_rect().encode(x=alt.X('Семейный статус:O', title='Сімейний статус'), y=alt.Y('Сословие, состояние или звание:O', title='Соціальна група'), color='count:Q', tooltip=['count']).properties(title = '1897', height = 200, width = 100).interactive()
In [262]:
alt.concat(status_heatmap_1778,status_heatmap_1897).properties(title = 'Співвідношення соціальної групи до сімейного статусу').resolve_scale(y='shared')
Out[262]:

Результат показує, що у 1778 році найпомітнішими групами були козаки та посполиті, що складали переважну більшість одружених осіб. У 1897 році це зберігається, проте змінюється характер розподілу: значно зростає кількість селян, які представлені серед одружених. З'являються нові соціальні групи, такі як містяни і дворяни, що вказує на зміну у соціальній структурі імперії протягом століття.

Національна структура і родини¶

Порівняння національної частки населення за походженням прізвищ¶

Проаналізувавши соціальну структуру родин села Дунаєць, я вирішила розширити дослідження та звернути увагу на зміну національної структури села. Це питання є актуальним, адже ліквідація козацького стану, процеси русифікації й утиску українців, а також початок індустріалізації дали дали шлях до переселення російської людності на українські території. Ось, яку інформацію мені вдалося знайти на вікіпедії

У світлі перепису 1897 (даних про кількість росіян в Україні до того часу нема) на українській суцільній етнографічній території жило 3,8 млн росіян на 27,8 млн всього населення, тобто вони становили 11,7 %

У зв'язку з відсутністю прямих вказівок на етнічну приналежність, я вирішила зробити орієнтовний аналіз походження прізвищ за суфіксами та іншими правилами, що вказують на його походження. Такий підхід є ще виправданим до XIX століття, оскільки прізвища у селян почали масово з'являтися у XVII ст., і протягом наступних двох століть не зазнавало великої асиміляції, зберігаючи зв'язок з національною ідентичністю.

Оскільки дані в переписах населення відрізняються, перше, що потрібно провести, це зведення прізвищ до "спільного знаменника". Логічно найзручнішим буде переклад прізвищ на українську мову. З даними перепису 1778 року буде простіше, оскільки потрібно замінити лише деякі букви (напр. И на початку слова на І) або суфікси (-ск- на -ськ-, -ин на -ін, але не вводити переклад и на і напряму, бо, наприклад, прізвище Лукашевич, Василенко не будуть перекладені як Лукашевіч, Васіленко)

In [263]:
pop_1778['surname'] = pop_1778['rus_surname'].replace(['ец', 'иш', 'ой', 'ье', 'ск', 'ие', 'И', 'ы', 'еенко', 'ин', 'ина'], ['ець', 'іш', 'ий', 'є', 'ськ', 'іє', 'І', 'и', 'ієнко', 'ін', 'іна'], regex = True)
pop_1778
Out[263]:
Unnamed: 0 Unnamed: 1 family_num Unnamed: 3 Unnamed: 4 Unnamed: 5 men Unnamed: 7 Unnamed: 8 Unnamed: 9 ... Unnamed: 13 status age Unnamed: 16 Unnamed: 17 Unnamed: 18 gender age_group fam_status surname
0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN None None NaN
1 Аркуш NaN NaN NaN Число людей наскрізне NaN Число людей джерело NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN None None NaN
2 NaN ID наскрізна ID домогосподарство Двори джерело Чоловіки Жінки Чоловіки Жінки Імʼя По-батькові ... Клас Соціальний статус Вік Був на сповіді Не був Не був за малолітством NaN None None Прізвище
3 445 1 1 1 1 NaN 1 NaN Федор Кондратьев ... 3b духовні 44 х NaN NaN m 40-44 одружений/а Лукашевич
4 NaN 2 NaN NaN NaN 1 NaN 1 Варвара Симева ... NaN духовні 35 х NaN NaN f 35-39 одружений/а NaN
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
1595 NaN 1593 104 12 824 NaN 236 NaN Леонтий Афанасиев ... 3a бездвірні 25 х NaN NaN m 25-29 одружений/а NaN
1596 NaN 1594 NaN NaN NaN 770 NaN 222 Мотрона Михайлова ... NaN бездвірні 22 х NaN NaN f 20-24 одружений/а NaN
1597 NaN 1595 105 13 825 NaN 237 NaN Герасим Фомик ... 3b бездвірні 43 х NaN NaN m 40-44 одружений/а Мерник
1598 NaN 1596 NaN NaN NaN 771 NaN 223 Агафия Максимова ... NaN бездвірні 42 х NaN NaN f 40-44 одружений/а NaN
1599 NaN 1597 NaN NaN 826 NaN 238 NaN Лукьян NaN ... NaN бездвірні 14 х NaN NaN m 10-14 None NaN

1600 rows × 23 columns

Для кожної особи надано значення прізвища

In [264]:
pop_1778['surname'] = pop_1778['surname'].ffill()
pop_1778
Out[264]:
Unnamed: 0 Unnamed: 1 family_num Unnamed: 3 Unnamed: 4 Unnamed: 5 men Unnamed: 7 Unnamed: 8 Unnamed: 9 ... Unnamed: 13 status age Unnamed: 16 Unnamed: 17 Unnamed: 18 gender age_group fam_status surname
0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN None None NaN
1 Аркуш NaN NaN NaN Число людей наскрізне NaN Число людей джерело NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN None None NaN
2 NaN ID наскрізна ID домогосподарство Двори джерело Чоловіки Жінки Чоловіки Жінки Імʼя По-батькові ... Клас Соціальний статус Вік Був на сповіді Не був Не був за малолітством NaN None None Прізвище
3 445 1 1 1 1 NaN 1 NaN Федор Кондратьев ... 3b духовні 44 х NaN NaN m 40-44 одружений/а Лукашевич
4 NaN 2 NaN NaN NaN 1 NaN 1 Варвара Симева ... NaN духовні 35 х NaN NaN f 35-39 одружений/а Лукашевич
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
1595 NaN 1593 104 12 824 NaN 236 NaN Леонтий Афанасиев ... 3a бездвірні 25 х NaN NaN m 25-29 одружений/а Кислая
1596 NaN 1594 NaN NaN NaN 770 NaN 222 Мотрона Михайлова ... NaN бездвірні 22 х NaN NaN f 20-24 одружений/а Кислая
1597 NaN 1595 105 13 825 NaN 237 NaN Герасим Фомик ... 3b бездвірні 43 х NaN NaN m 40-44 одружений/а Мерник
1598 NaN 1596 NaN NaN NaN 771 NaN 223 Агафия Максимова ... NaN бездвірні 42 х NaN NaN f 40-44 одружений/а Мерник
1599 NaN 1597 NaN NaN 826 NaN 238 NaN Лукьян NaN ... NaN бездвірні 14 х NaN NaN m 10-14 None Мерник

1600 rows × 23 columns

Посилаючись на те, що важливим є саме переклад прізвища, колонку "ФИО" в переписі 1897 року було поділено на колонки з прізвищем, ім'ям і побатькові.

In [265]:
pop_1897['surname_og'] = pop_1897['ФИО'].str.split(expand = True)[0].str.capitalize()
pop_1897['name'] = pop_1897['ФИО'].str.split(expand = True)[1].str.capitalize()
pop_1897['middle_name'] = pop_1897['ФИО'].str.split(expand = True)[2].str.capitalize()
pop_1897
Out[265]:
Кто заполнял базу ID строки в базе List ID Link ID Домохозяйствао ID жилец ФИО gender Глава хозяйства и глава семьи age ... Умеет ли читать Обучение Профессия главное Профессия вспомогательное Положение по воинской повинности Примітки age_group surname_og name middle_name
0 Podhorna 1.0 F.132.Op.1.Spr.1343. Арк.1-2 https://www.familysearch.org/ark:/61903/3:1:3Q... 1.0 1.0 Krasovskij Pantelejmon Ivanovich m husband 32 ... 1.0 rural school farmer NaN Nizhnij chin zapasu NaN 30-34 Krasovskij Pantelejmon Ivanovich
1 Podhorna 2.0 F.132.Op.1.Spr.1343. Арк.1-2 https://www.familysearch.org/ark:/61903/3:1:3Q... 1.0 2.0 Krasovskaya Stefanida Artemieva f wife 28 ... 0.0 NaN farmer with husband NaN NaN NaN 25-29 Krasovskaya Stefanida Artemieva
2 Podhorna 3.0 F.132.Op.1.Spr.1343. Арк.1-2 https://www.familysearch.org/ark:/61903/3:1:3Q... 1.0 3.0 Krasovskaya Anna Pantelejmonova f daughter 5 ... 0.0 NaN with father NaN NaN NaN 5-9 Krasovskaya Anna Pantelejmonova
3 Podhorna 4.0 F.132.Op.1.Spr.1343. Арк.1-2 https://www.familysearch.org/ark:/61903/3:1:3Q... 1.0 4.0 Krasovskij Stefan Pantelejmonov m son 1 ... 0.0 NaN with father NaN NaN NaN 0-4 Krasovskij Stefan Pantelejmonov
4 Podhorna 5.0 F.132.Op.1.Spr.1343. Арк.1-2 https://www.familysearch.org/ark:/61903/3:1:3Q... 1.0 5.0 Krasovskij Ivan Kondratov m father 70 ... 0.0 NaN with son NaN NaN Slep na oba hlaza 10 let nazad 70-74 Krasovskij Ivan Kondratov
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
1547 Podhorna 1548.0 F.132.Op.1.Spr.1343. Арк.550-551 https://www.familysearch.org/ark:/61903/3:1:3Q... 261.0 6.0 Gricunov Mikhail Sergeev m son 3 ... 0.0 NaN with father NaN NaN NaN 0-4 Gricunov Mikhail Sergeev
1548 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN None NaN NaN NaN
1549 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN None NaN NaN NaN
1550 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN None NaN NaN NaN
1551 * - В оригінальному джерелі ячейка "мова" була... NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN None NaN NaN NaN

1552 rows × 29 columns

Для перекладу прізвищ з перепису 1897 року використано pd.translate(str.maketrans()) і pd.replace(). Другий для деяких суфіксів і літер, які не мають прямих відповідників англійською, а після цього перший, щоб перекласти те, що залишилось

In [266]:
mytable = str.maketrans({'w': 'в', 'e': 'е', 'r': 'р', 't': 'т', 'y': 'и','u': 'у', 'i': 'и', 'o': 'о', 'p': 'п', 'a': 'а', 's': 'с', 'd': 'д', 'f': 'ф', 'g': 'г', 'h': 'г', 'j': 'й', 'k': 'к', 'l': 'л', 'z': 'з', 'c': 'с', 'v': 'в','b': 'б', 'n': 'н', 'm': 'м', "'": 'ь'})
pop_1897['two_let'] = pop_1897['surname_og'].replace(['ina', 'in', 'eenko', 'aya', 'yj', 'ya', 'esh', 'shch', 'ch', 'yi', 'ye', 'yu', 'ts', 'sh', 'zh', 'kh', 'ai', 'sk', 'ij', 'ie', 'Ya', 'Ye', 'Yi', 'Yu', 'I', 'E', 'C'], ['іна', 'ін', 'ієнко', 'а', 'ий', 'я', 'іш', 'щ', 'ч', 'ї', 'є', 'ю', 'ц', 'ш', 'ж', 'х', 'аї', 'ськ', "ий", 'іє', 'Я', 'Є', 'Ї', 'Ю', 'І', "Є", 'Ц'], regex = True)

pop_1897['surname'] = pop_1897['two_let'].str.lower().str.translate(mytable).str.capitalize()
pop_1897 = pop_1897.drop(columns = ['surname_og', 'two_let'])
pop_1897
Out[266]:
Кто заполнял базу ID строки в базе List ID Link ID Домохозяйствао ID жилец ФИО gender Глава хозяйства и глава семьи age ... Умеет ли читать Обучение Профессия главное Профессия вспомогательное Положение по воинской повинности Примітки age_group name middle_name surname
0 Podhorna 1.0 F.132.Op.1.Spr.1343. Арк.1-2 https://www.familysearch.org/ark:/61903/3:1:3Q... 1.0 1.0 Krasovskij Pantelejmon Ivanovich m husband 32 ... 1.0 rural school farmer NaN Nizhnij chin zapasu NaN 30-34 Pantelejmon Ivanovich Красовський
1 Podhorna 2.0 F.132.Op.1.Spr.1343. Арк.1-2 https://www.familysearch.org/ark:/61903/3:1:3Q... 1.0 2.0 Krasovskaya Stefanida Artemieva f wife 28 ... 0.0 NaN farmer with husband NaN NaN NaN 25-29 Stefanida Artemieva Красовська
2 Podhorna 3.0 F.132.Op.1.Spr.1343. Арк.1-2 https://www.familysearch.org/ark:/61903/3:1:3Q... 1.0 3.0 Krasovskaya Anna Pantelejmonova f daughter 5 ... 0.0 NaN with father NaN NaN NaN 5-9 Anna Pantelejmonova Красовська
3 Podhorna 4.0 F.132.Op.1.Spr.1343. Арк.1-2 https://www.familysearch.org/ark:/61903/3:1:3Q... 1.0 4.0 Krasovskij Stefan Pantelejmonov m son 1 ... 0.0 NaN with father NaN NaN NaN 0-4 Stefan Pantelejmonov Красовський
4 Podhorna 5.0 F.132.Op.1.Spr.1343. Арк.1-2 https://www.familysearch.org/ark:/61903/3:1:3Q... 1.0 5.0 Krasovskij Ivan Kondratov m father 70 ... 0.0 NaN with son NaN NaN Slep na oba hlaza 10 let nazad 70-74 Ivan Kondratov Красовський
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
1547 Podhorna 1548.0 F.132.Op.1.Spr.1343. Арк.550-551 https://www.familysearch.org/ark:/61903/3:1:3Q... 261.0 6.0 Gricunov Mikhail Sergeev m son 3 ... 0.0 NaN with father NaN NaN NaN 0-4 Mikhail Sergeev Грисунов
1548 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN None NaN NaN NaN
1549 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN None NaN NaN NaN
1550 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN None NaN NaN NaN
1551 * - В оригінальному джерелі ячейка "мова" була... NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN None NaN NaN NaN

1552 rows × 29 columns

In [267]:
pop_1897['surname'].unique()
Out[267]:
array(['Красовський', 'Красовська', 'Скоропад', 'Скоропадова',
       'Беловська', 'Беловський', 'Старченко', 'Бобровийський',
       'Бобровийська', 'Лукаш', 'Лукашова', 'Михнюк', 'Бортникова',
       'Дручок', 'Дручкова', 'Знобей', 'Знобеева', 'Двореский',
       'Мартіненко', 'Сачков', 'Сачкова', 'Бортник', 'Малафієнко',
       'Петрик', 'Петрикова', 'Печений', 'Печена', 'Соломяний', 'Рудич',
       'Рибалка', 'Рибалкіна', 'Гаевий', 'Гаева', 'Позняк', 'Познякова',
       'Давиденко', 'Сгульга', 'Сгульгіна', 'Гаврик', 'Гаврикова',
       'Сапожников', 'Сапожникова', 'Контрибус', 'Контрибусева',
       'Созоненкова', 'Ясуков', 'Ясукова', 'Комаров', 'Комарова',
       'Ляшенко', 'Оничко', 'Манжос', 'Манжосова', 'Пташник',
       'Пташникова', 'Корсун', 'Корсунова', 'Юша', 'Юшіна', 'Багнюков',
       'Багнюкова', 'Сгчербак', 'Кулибакіна', 'Кулибака', 'Гайдукова',
       'Соломений', 'Мищенко', 'Гайдук', 'Воробей', 'Воробйова',
       'Сковорода', 'Сковородіна', 'Ядутін', 'Ядутіна', 'Горкун',
       'Горкунова', 'Івансов', 'Івансова', 'Козел', 'Козлова', 'Цигикал',
       'Цигикалова', 'Козлов', 'Василий', 'Мукорез', 'Мукорезова',
       'Костюченкова', 'Рожен', 'Рожнова', 'Тищенко', 'Новобранний',
       'Красовиский', 'Бирінова', 'Бирін', 'Бабенкова', 'Соломяна',
       'Ігнатенко', 'Дьяченко', 'Лобас', 'Лобасова', 'Воробьева', 'Очкал',
       'Очкалова', 'Мехедкова', 'Мехедок', 'Березний', 'Березна',
       'Гуреев', 'Гуреева', 'Юшін', 'Сгумейко', 'Онопрієнко', 'Бабич',
       'Бабичева', 'Противен', 'Колоша', 'Колошіна', 'Моськовченко',
       'Кузнесов', 'Кузнесова', 'Малафієнкова', 'Вороей', 'Мороз',
       'Морозова', 'Довгополий', 'Довгопола', 'Николай', 'Овсієнко',
       'Мигаїл', 'Іван', 'Божок', 'Божкова', 'Белоусова', 'Оноденко',
       'Купіна', 'Фалева', 'Макаренко', 'Кушнеров', 'Кушнерова',
       'Овчаренко', nan, 'Овсієнкова', 'Науменко', 'Рой', 'Роева',
       'Таратінова', 'Науменкова', 'Понурко', 'Половінка', 'Прилуска',
       'Петр', 'Тищенкова', 'Опанасенко', 'Кириченко', 'Громакова',
       'Дятел', 'Середа', 'Закройсева', 'Кулішова', 'Куліш',
       'Лавренченко', 'Кульбакіна', 'Малофієнко', 'Вегеза', 'Оксененко',
       'Мигаленко', 'Лобасенко', 'Єременко', 'Приданова', 'Згуравлева',
       'Цгеркасова', 'Бойко', 'Савиский', 'Сависка', 'Старченкова',
       'Белявський', 'Белявська', 'Таратіна', 'Таратін', 'Павловський',
       'Грисунов', 'Грисунова'], dtype=object)

Далі йде класифікація. У ній я створила 2 набори суфіксів різних походжень і також список професій, в честь яких існує багато українських прізвищ

In [268]:
ukr_suffixes_re = r'ук$|юк$|ів$|ець$|ик$|ко$|ар$|яр$|(?<!ов)ський$|(?<!ов)ська$|ень$|ич$|йч$|ун$|яний$|яна$|ений$|ена$|ок$|аевий$|аева$|иха$|ака$|ак$|аль$|ач$'
some_ukr_job_surnames = ['Коваль', 'Ткач', 'Стельмах', "Кожум'яка", 'Драч', 'Лукаш', 'Скоропад', 'Куліш', 'Середа']
rus_suffixes_re = r'ов$|ев$|ін$|ова$|ева$|іна$|єв$|єва$|овський$|овська$|овській$|ськой$|ськая$|ский$|ин$|ина$|ая$'

Функції ukr_mask(df) та rus_mask(df), де df -- відповідний датафрейм, створені для фільтрації датасетів відповідно до суфіксів і списку наведених вище

In [269]:
def ukr_mask(df):
    mask = df['surname'].str.contains(ukr_suffixes_re, regex = True, na = False) | (df['surname'].isin(some_ukr_job_surnames))
    return mask
def rus_mask(df):
    mask = df['surname'].str.contains(rus_suffixes_re, regex = True, na = False)
    return mask

Було введено ще інші прізвища, в які або не можна чітко класифікувати й вони потребують окремого аналізу, або мають помилки в написанні

In [270]:
def surnames_classification(df):
    ukr_surnames = df[ukr_mask(df)]
    rus_surnames = df[rus_mask(df)]
    other_surnames = df[~(rus_mask(df) | ukr_mask(df))]
    return (ukr_surnames, rus_surnames, other_surnames)
In [271]:
ukr_surnames_1778 = surnames_classification(pop_1778)[0]
rus_surnames_1778 = surnames_classification(pop_1778)[1]
other_surnames_1778 = surnames_classification(pop_1778)[2]
In [272]:
ukr_surnames_1897 = surnames_classification(pop_1897)[0]
rus_surnames_1897 = surnames_classification(pop_1897)[1]
other_surnames_1897 = surnames_classification(pop_1897)[2]

Для візуалізації цього розподілу я побудувала пайчарт, виористовуючи бібліотеку matplotlib

In [273]:
origins = ['Українські', 'Російські','Інші']

fig, ax = plt.subplots(1,2, sharey = True)
fig.suptitle("Походження прізвищ")
colors = ['gold', 'salmon', 'mediumspringgreen']

ax[0].pie([len(ukr_surnames_1778), len(rus_surnames_1778), len(other_surnames_1778)], labels = origins, autopct='%1.1f%%', startangle=140, colors = colors)
ax[0].set_title('1778')

ax[1].pie([len(ukr_surnames_1897), len(rus_surnames_1897), len(other_surnames_1897)], labels = origins, autopct='%1.1f%%', startangle=140, colors = colors)
ax[1].set_title('1897')

plt.show()
No description has been provided for this image

Більшість прізвищ все-таки класифікувались, що дало приблизно однакову частку прізвищ "іншого" походження. Результати візуалізації показують, що частка росіян істотно збільшилась на 20%, охопивши таку ж частку українців, які, станом на 1778 рік, становили 2/3 населення.

Родини, що проживали у селі на час обох переписів, та зміна їх чисельності¶

Отримавши дані, про національні частки населення, я провела аналіз родин, які мешкали у селі Дунаєць протягом століття проміжку між переписами, і як змінилась їх чисельність. Для початку було створено датафрейми з усіма прізвищами і їхньою кількістю

In [274]:
surnames_1778 = pop_1778['surname'].value_counts().reset_index()
surnames_1778
Out[274]:
surname count
0 Старченко 71
1 Покровський 52
2 Науменко 44
3 Цикаль 44
4 Бортник 41
... ... ...
99 Лобач 4
100 Прокопець 3
101 Мерник 3
102 Ковалиха 3
103 Прізвище 1

104 rows × 2 columns

In [275]:
surnames_1897 = pop_1897['surname'].value_counts().reset_index()
surnames_1897
Out[275]:
surname count
0 Старченко 188
1 Цигикал 85
2 Цигикалова 78
3 Овсієнко 78
4 Давиденко 51
... ... ...
176 Горкунова 1
177 Ядутіна 1
178 Таратінова 1
179 Ядутін 1
180 Новобранний 1

181 rows × 2 columns

Після цього дані з обох таблиць поєднано внутрішньо за прізвищем, а також створено окрему колоку diff з десятковими значеннями різниці (різниця того, скільки друге значення становить від першого). Прізвища були посортовані за значенням diff для зручності й читабельності

In [276]:
common_surnames = surnames_1778.merge(surnames_1897, on = 'surname', how = 'inner')
common_surnames.rename(columns = {'count_x': '1778', 'count_y': '1897'}, inplace = True)
common_surnames['diff'] = ((common_surnames['1897']*100/(common_surnames['1778']) - 100)/100).round(3)
common_surnames.sort_values(by = ['diff'], ascending = False, inplace = True)
sorting = common_surnames['surname'].to_list()

Переведено в довгий формат для візуалізації

In [277]:
common_surnames_melt = common_surnames.melt(id_vars='surname', var_name='year', value_name='count')
common_surnames_melt
Out[277]:
surname year count
0 Овсієнко 1778 24.000
1 Давиденко 1778 16.000
2 Бирін 1778 8.000
3 Старченко 1778 71.000
4 Козел 1778 4.000
... ... ... ...
79 Корсун diff -0.718
80 Гаевий diff -0.730
81 Середа diff -0.818
82 Бортник diff -0.951
83 Ігнатенко diff -0.968

84 rows × 3 columns

Для візуалізації цього явища використовуватиму Ranged Dot Plot за допомогою бібліотеки Altair. Біля кожного прізвища стоятиме відсоток зміни відповідного кольору: червоний, якщо від'ємний, зелений -- додатний. Для зручності в орієнтації років використала розрізнення не тільки за кольором точок, а й за формою. Можна навести на точку і подивитися, саму чисельність

In [278]:
common_surnames['x_pos'] = common_surnames[['1778', '1897']].max(axis=1)
chart = (alt.Chart(common_surnames_melt).encode(x=alt.X("count:Q", title = 'Кількість'), y=alt.Y("surname:N", title = 'Прізвище', sort = sorting), ).transform_filter(alt.FieldOneOfPredicate(field="surname",oneOf=common_surnames['surname'],)).transform_filter(alt.FieldOneOfPredicate(field="year", oneOf=[1778, 1897])).properties(width=500, height=400, title = 'Зміна розміру родин, які проживали в селі Дунаєць, за обома переписами'))

line = chart.mark_line(color="#e8c296").encode(detail="surname:N")

color = alt.Color("year:O", title='Рік').scale(domain=[1778, 1897], range=["blue", "orange"])
points = (chart.mark_point(size=100,opacity=1,filled=True,).encode(color=color, tooltip = alt.Tooltip(['count'], title = "Кількість"), shape=alt.Shape('year', title= 'Рік').scale(range = ['circle', "triangle"])).interactive())

text = alt.Chart(common_surnames).mark_text(align="left",baseline="middle", dx=10).encode(y=alt.Y('surname:N', sort=sorting),x=alt.X('x_pos:Q'),text=alt.Text('diff:Q', format='.1%'), color=alt.condition(alt.datum['1897'] > alt.datum['1778'] ,alt.value('green'), alt.value('crimson')))
(line + points + text)
Out[278]:

З результатів випливає, що чисельність більшості родин зменшилась, причиною чого може бути не так зменшення народжуваності(а вона була доволі високою), як виїзд родин за межі села, наприклад, у пошуках роботи або в місто. Найбільш зросла чисельність родини Освієнків, Давиденків та Биріних, за самою ж чисельністю місце залишається за родиною Старченків. Натомість, з родини Ігнатенків у 1897 році залишилась лише одна особа. В інтернеті можна знайти такі дані:

Весною 1917 року в село приїхали три чоловіки з Глухова. Староста зібрав сход. Прибулі розповіли про зміни в країні, про те, що царя скинуто, що є Тимчасовий уряд. На сході було обрано комітет уповноважених, який повинен був керувати в селі. До нього увійшли Цигикал Кирило Самосіянович, Овсієнко Семен Степанович, Давиденко Михайло та інші.

При проведенні підрахунку родин вище виявилось, що родина Цигикалів є другою найчисельнішою після Старченків.

In [279]:
surnames_1897[(surnames_1897['surname'] == 'Цигикал') | (surnames_1897['surname'] == 'Цигикалова')].sum(axis = 0)
Out[279]:
surname    ЦигикалЦигикалова
count                    163
dtype: object

Висновок¶

У цьому дослідженні було здійснено аналіз демографічної, соціальної та національно-структурної ситуації села Дунаєць Чернігівської губернії за даними переписів 1778 та 1897 років. Серед основних аспектів можна виділити:

  • у віковій структурі: збільшення середньої тривалості життя, зменшення дитячої смертності, загальна симетричність між кількісним складом за статтю;
  • у соціальній структурі: велика частка козацьких сімей, яка кількісно залишалась провідною. До того ж, зріс рівень заміжжя серед козаків та їх родин і відбулося зникнення одних (посполиті й бездвірні) і поява нових соціальних структур, таких як містяни і дворяни;
  • у національній структурі: зростання частки росіян у структурі, зменшення чисельності родин, що проживали в селі на момент 1778 року, провідна роль найчисельніших родин у справах села.

Таким чином, проєкт відображає не лише чисельну зміну показників, а й відображає соціально-культурні трансформації в межах села Дунаєць.